package cn.fx.framework.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;

import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ljh
 * @date 2018-08-19 15:59
 */
public class RedisCacheWrapper implements Cache {
    private static final Logger log = LoggerFactory.getLogger(RedisCacheWrapper.class);

    private MyRedisCacheManager cacheManager;

    private final Cache cache;

    private Map<String,Object> localCacheMap = new ConcurrentHashMap<>();

    RedisCacheWrapper(MyRedisCacheManager cacheManager, Cache cache) {
        this.cacheManager = cacheManager;
        this.cache = cache;
    }

    @Override
    public String getName() {
        // log.info("name: {}", cache.getName());
        try {
            return cache.getName();
        } catch (Exception e) {
            log.error("getName ---> errmsg: {}", e.getMessage(), e);
            return null;
        }
    }

    @Override
    public Object getNativeCache() {
        // log.info("nativeCache: {}", cache.getNativeCache());
        try {
            return cache.getNativeCache();
        } catch (Exception e) {
            log.error("getNativeCache ---> errmsg: {}", e.getMessage(), e);
            return null;
        }
    }

    @Override
    public ValueWrapper get(Object o) {
        // log.info("get ---> o: {}", o);
        try {
            //缓存key
            String key = o.toString();
            ValueWrapper vw;
            if(isEnableLocalCache(key)){
                //在本地缓存中获取缓存值
                Object objValue = localCacheMap.get(key);
                if(objValue != null && objValue instanceof ValueWrapper){
                    //本地有缓存项，则返回
                    return  (ValueWrapper)localCacheMap.get(key);
                }
                //从底层缓存中获取
                vw = cache.get(o);
                if(vw == null){
                    return null;
                }
                //将底层取到的数据加入本地缓存中
                localCacheMap.put(key,vw);
            }else{
                vw = cache.get(o);
            }
            return vw;
        } catch (Exception e) {
            log.error("get ---> o: {}, errmsg: {}", o, e.getMessage(), e);
            return null;
        }
    }

    @Override
    public <T> T get(Object o, Class<T> aClass) {
        // log.info("get ---> o: {}, clazz: {}", o, aClass);
        try {
            String key = o.toString();
            if(isEnableLocalCache(key)){
                Object objValue = localCacheMap.get(key);
                if(objValue != null && aClass.isInstance(objValue)){
                    return (T)objValue;
                }
                T t = cache.get(o, aClass);
                if(t != null){
                    localCacheMap.put(key,objValue);
                }
                return t;
            }else{
                return cache.get(o,aClass);
            }
        } catch (Exception e) {
            log.error("get ---> o: {}, clazz: {}, errmsg: {}", o, aClass, e.getMessage(), e);
            return null;
        }
    }

    @Override
    public <T> T get(Object o, Callable<T> callable) {
        // log.info("get ---> o: {}", o);
        try {
            String key = o.toString();
            if(isEnableLocalCache(key)) {
                Object objValue = localCacheMap.get(key);
                if (objValue != null) {
                    return (T) objValue;
                }
                T t = cache.get(o, callable);
                if (t != null) {
                    localCacheMap.put(key, objValue);
                }
                return t;
            }else{
                return cache.get(o,callable);
            }
        } catch (Exception e) {
            log.error("get ---> o: {}, errmsg: {}", o, e.getMessage(), e);
            return null;
        }
    }

    @Override
    public void put(Object cacheKey, Object cacheValue) {
        // log.info("put ---> o: {}, o1: {}", o, o1);
        try {
            cache.put(cacheKey, cacheValue);
            String key = cacheKey.toString();
            if(isEnableLocalCache(key)){
                removeLocalCache(key); //??
                //localCacheMap.put(cacheKey.toString(),cacheValue); //这样才对,存在？2024-9-28
            }
        } catch (Exception e) {
            log.error("put ---> key: {}, value: {}, errmsg: {}", cacheKey, cacheValue, e.getMessage(), e);
        }
    }

    @Override
    public ValueWrapper putIfAbsent(Object o, Object o1) {
        // log.info("putIfAbsent ---> o: {}, o1: {}", o, o1);
        try {
            return cache.putIfAbsent(o, o1);
        } catch (Exception e) {
            log.error("putIfAbsent ---> o: {}, o1: {}, errmsg: {}", o, o1, e.getMessage(), e);
            return null;
        }
    }

    @Override
    public void evict(Object o) {
        // log.info("evict ---> o: {}", o);
        try {
            String key = o.toString();
            if(isEnableLocalCache(key)){
                removeLocalCache(key);
            }
            cache.evict(o);
        } catch (Exception e) {
            log.error("evict ---> o: {}, errmsg: {}", o, e.getMessage(), e);
        }
    }

    @Override
    public void clear() {
        // log.info("clear");
        try {
            //先清除本地缓存
            clearLocalCache();
            cache.clear();
        } catch (Exception e) {
            log.error("clear ---> errmsg: {}", e.getMessage(), e);
        }
    }

    public void removeLocalCache(String key){
        localCacheMap.remove(key);
    }

    public void clearLocalCache(){
        localCacheMap.clear();
    }

    boolean enableLocalCache = false;

    public boolean isEnableLocalCache(String key){
        if(enableLocalCache){
            return true;
        }
        //检测本缓存实例是否支持
        if(MyRedisCacheManager.isEnableLocalCache(this.getName())){
            enableLocalCache = true;
            return true;
        }
        return MyRedisCacheManager.isEnableLocalCache(key);
    }
}
